package com.gupaoedu.mvcframework.servlet;

import com.gupaoedu.mvcframework.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GPDispatcherServlet extends HttpServlet{

    private Properties p = new Properties();

    private List<String> classNames = new ArrayList<String>();

    private Map<String,Object> ioc = new HashMap<String,Object>();

//    private Map<String,Method> handlerMapping = new HashMap<String,Method>();

    private List<Handler> handlerMapping = new ArrayList<Handler>();


    //初始化
    @Override
    public void init(ServletConfig config) throws ServletException {
        //1、加载配置文件
        doLoadConfig("application.properties");

        //2 扫描所有相关
        doScanner(p.getProperty("scanPackage"));


        //3 将所有相关class的实例初始化，并且将其保存到IOC容器中
        doInstance();

        //4、 自动化依赖注入
        doAutowired();

        //5 初始化handlerMapping
        initHandlerMapping();

        System.out.println("GuPao MVC FrameWork is inited.");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req, resp);
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    private void doLoadConfig(String location){
        InputStream ins = this.getClass().getClassLoader().getResourceAsStream(location);

        try{
            p.load(ins);
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try{
                if(null!=ins)
                    ins.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    private void doScanner(String packageName){
        URL url = this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.","/"));
        File classesDir = new File(url.getFile());

        for(File file : classesDir.listFiles()){
            if(file.isDirectory()){
                doScanner(packageName+"."+file.getName());
            }
            else{
                classNames.add(packageName + "."+ file.getName().replace(".class",""));
            }
        }
    }

    private void doInstance(){
        if(classNames.isEmpty()){
            return;
        }

        for(String className: classNames){
            try {
               Class<?> clazz = Class.forName(className);

               //进行实例化，原则问题
                //判断，不是所有的都实例化，只有加了GPController、GPService等才实例化

                if(clazz.isAnnotationPresent(GPContoller.class)){

                    String beanName = lowerFirst(clazz.getSimpleName());

                    ioc.put(beanName, clazz.newInstance());

                }
                else if(clazz.isAnnotationPresent(GPService.class)){
                    // beanName beanId
                    //1、默认采用类名的首字母小写



                    GPService service = clazz.getAnnotation(GPService.class);
                    String beanName = service.value();

                    //2、如果自己定义了名字的话，优先使用自己定义的名字
                    if("".equals(beanName.trim())){
                        beanName = lowerFirst(clazz.getSimpleName());
                    }

                    Object instance = clazz.newInstance();

                    ioc.put(beanName,instance);

                    //3.根据类型匹配，利用接口作为key （多继承？？？）
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for(Class<?> i:interfaces){
                        ioc.put(i.getName(),instance);
                    }

                }
                else{
                    continue;
                }

            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    private void doAutowired(){

        if(ioc.isEmpty()) return;

        for(Map.Entry<String,Object> entry: ioc.entrySet()){

            // 识别 @GPAutowired
            Field[] fields = entry.getValue().getClass().getDeclaredFields();


            for(Field field: fields){
                if(!field.isAnnotationPresent(GPAutowired.class)){
                    continue;
                }

                GPAutowired autowired = field.getAnnotation(GPAutowired.class);
                String beanName = autowired.value().trim();

                if("".equals(beanName)){
                    beanName = field.getType().getName();
                }

                field.setAccessible(true); // 暴力反射

                try {
                    field.set(entry.getValue(), ioc.get(beanName));
                }catch (Exception e){

                    e.printStackTrace();

                }


            }



        }

    }

    private void initHandlerMapping(){
        if(ioc.isEmpty()) return;

        for(Map.Entry<String,Object> entry : ioc.entrySet()){
            Class<?> clazz = entry.getValue().getClass();

            //HandlerMapping 只认识@GPController
            if(!clazz.isAnnotationPresent(GPContoller.class)){
                continue;
            }

            String url = "";
            if(clazz.isAnnotationPresent(GPRequestMapping.class)){
                GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
                url = requestMapping.value();
            }

//            Method[] methods = clazz.getMethods();
//
//            for(Method method: methods){
//                if(!method.isAnnotationPresent(GPRequestMapping.class)){
//                    continue;
//                }
//
//                GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
//
//                String murl = url + requestMapping.value();
//
//                handlerMapping.put(murl,method);
//
//                System.out.println(murl+":"+method);
//            }

            //获取Method的URL配置
            Method[] methods = clazz.getMethods();
            for(Method method: methods){
                //没有加RequestMapping注解的直接忽略
                if(!method.isAnnotationPresent(GPRequestMapping.class)){
                    continue;
                }
                try {
                    //映射URL
                    GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
                    String regex = ("/" + url + requestMapping.value()).replaceAll("/+", "/");
                    Pattern patter = Pattern.compile(regex);
                    handlerMapping.add(new Handler(entry.getValue(), method, patter));
                }catch (Exception e)
                {
                    System.out.println(e.toString());
                }

            }


        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{
//        String url = req.getRequestURI();
//
//        String contextPath = req.getContextPath();
//
//        url = url.replace(contextPath,"").replaceAll("/+","/");
//
//        if(!handlerMapping.containsKey(url)){
//            resp.getWriter().write("404 not Found");
//            return ;
//        }
//
//        System.out.println("获取对应的方法："+handlerMapping.get(url));
//
//        Method method = handlerMapping.get(url);
//
//        Object obj = method.invoke(obj, args);

        try{
            Handler handler = getHandler(req);
            if(handler == null){
                //如果没有匹配上，返回404错误
                resp.getWriter().write("404 not Found");
                return ;
            }

            //获取方法参数列表
            Class<?> [] paramTypes = handler.method.getParameterTypes();

            //保存所有需要自动赋值的参数值
            Object[] paramValues = new Object[paramTypes.length];

            Map<String, String[]> params = req.getParameterMap();

            for(Map.Entry<String,String[]> param: params.entrySet()){
                String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]","").replaceAll(",\\s",",");
                //如果找到匹配的对象，则开始填充数值
                if(!handler.paramIndexMapping.containsKey(param.getKey()))
                    continue;
                int index = handler.paramIndexMapping.get(param.getKey());
                paramValues[index] = convert(paramTypes[index],value);
            }

            //设置方法中的request和response对象
            int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[reqIndex] = req;
            int respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[respIndex] = resp;

            handler.method.invoke(handler.controller, paramValues);

        }catch (Exception e){
            throw e;
        }
    }

    private Handler getHandler(HttpServletRequest req) throws Exception{
        if(handlerMapping.isEmpty()) return null;
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+","/");
        for(Handler handler: handlerMapping){
            try{
                Matcher matcher = handler.pattern.matcher(url);
                if(!matcher.matches())
                    continue;
                return handler;
            }catch (Exception e){
                throw  e;
            }
        }
        return null;
    }

    private Object convert(Class<?> type, String value){
        if(Integer.class == type){
            return Integer.valueOf(value);
        }
        return value;
    }

    private String lowerFirst(String str){
        char[] chars = str.toCharArray();
        chars[0]+=32;
        return String.valueOf(chars);
    }



    private class Handler{

        protected Object controller; // 保存方法对应的实例
        protected Method method; //保存映射的方法
        protected Pattern pattern;
        protected Map<String, Integer> paramIndexMapping; //参数顺序

        public Handler(Object controller, Method method, Pattern pattern) {
            this.controller = controller;
            this.method = method;
            this.pattern = pattern;

            paramIndexMapping = new HashMap<String, Integer>();
            putParamIndexMapping(method);
        }

        public void putParamIndexMapping(Method method){
            //提取方法中加了注解的参数
            Annotation [][] pa = method.getParameterAnnotations();
            for(int i = 0; i<pa.length; i++){
                for(Annotation a: pa[i]){
                    if(a instanceof GPRequestParam){
                        String paramName = ((GPRequestParam)a).value();
                        if(!"".equals(paramName.trim())){
                            paramIndexMapping.put(paramName,i);
                        }
                    }
                }
            }

            //提取方法中的request和response参数
            Class<?>[] paramsTypes = method.getParameterTypes();
            for(int i=0; i< paramsTypes.length; i++){
                Class<?> type = paramsTypes[i];
                if(type == HttpServletRequest.class ||
                    type == HttpServletResponse.class){
                    paramIndexMapping.put(type.getName(),i);
                }
            }

        }
    }
}
